package org.budo.permission.config;

import java.util.List;

import org.budo.dubbo.protocol.http.aop.permission.user.AbstractUserPermissionCheckService;
import org.budo.dubbo.protocol.http.aop.permission.user.UserPermissionCheckHandlerInterceptor;
import org.budo.dubbo.protocol.http.aop.permission.user.UserPermissionCheckMethodInterceptor;
import org.budo.dubbo.protocol.http.aop.permission.user.UserPermissionCheckService;
import org.budo.permission.filter.BudoPermissionSupportFilter;
import org.budo.support.lang.util.StringUtil;
import org.budo.support.spring.aop.advisor.BudoAopPointcutAdvisor;
import org.budo.support.spring.bean.factory.support.BeanBuilder;
import org.budo.support.spring.context.aware.RootApplicationContextRefreshedEventListener;
import org.budo.support.spring.util.SpringUtil;
import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.util.ObjectUtils;
import org.springframework.web.servlet.handler.MappedInterceptor;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

/**
 * @author lmw
 * 
 * @see org.budo.spring.velocity.view.BudoVelocityLayoutView#createVelocityContext(Map<String,Object>)
 * @see org.budo.graph.config.BudoGraphConfigurer#registryWebInterceptor(BeanDefinitionRegistry)
 */
@Slf4j
@Getter
@Setter
@ToString
public class BudoPermissionConfigurer extends RootApplicationContextRefreshedEventListener implements BeanDefinitionRegistryPostProcessor {
    /**
     * 当前应用身份
     */
    private String appKey;

    /**
     * 指定Aop过滤的作用范围
     */
    private String aopPatterns;

    /**
     * 指定Mvc过滤的作用范围
     */
    private String mvcPatterns;

    private Class<?> userPermissionCheckServiceType = AbstractUserPermissionCheckService.class;

    /**
     * @see #org.budo.graph.config.BudoGraphConfigurer#postProcessBeanDefinitionRegistry(BeanDefinitionRegistry)
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        log.info("#25 postProcessBeanDefinitionRegistry, registry=" + ObjectUtils.identityToString(registry));

        this.registryWebInterceptor(registry);
    }

    protected void onRootApplicationContextRefreshedEvent(ContextRefreshedEvent contextRefreshedEvent) {
        log.info("#14 onRootApplicationContextRefreshedEvent, this=" + this);

        ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext();
        this.registerPermissionApiFilter(applicationContext);

        this.registryUserPermissionCheckService(applicationContext);

        this.registryAopInterceptor(applicationContext);
    }

    protected void registryUserPermissionCheckService(ApplicationContext applicationContext) {
        List<BeanDefinition> definitions = SpringUtil.getBeanDefinitionListByType(applicationContext, UserPermissionCheckService.class.getName());
        if (null != definitions && !definitions.isEmpty()) {
            log.info("#139 UserPermissionCheckService beanDefinitions=" + definitions);
            return;
        }

        new BeanBuilder().id("UserPermissionCheckService") //
                .type(AbstractUserPermissionCheckService.class) //
                .registerTo(applicationContext);
    }

    protected void registryAopInterceptor(ApplicationContext applicationContext) {
        if (StringUtil.isNullOrEmpty(this.getAopPatterns())) {
            log.error("#56 registryAopInterceptor return aopPatterns is null, this=" + this);
            return;
        }

        new BeanBuilder() //
                .id("UserPermissionCheckMethodInterceptor") //
                .type(UserPermissionCheckMethodInterceptor.class) //
                .registerTo(applicationContext);

        // 自动Aop
        if ("auto".equalsIgnoreCase(this.getAopPatterns())) {
            new BeanBuilder() //
                    .id("budo-permission-advisor") //
                    .type(BudoAopPointcutAdvisor.class) //
                    .ref("advice", "UserPermissionCheckMethodInterceptor") //
                    .registerTo(applicationContext);
        } else { // 指定Aop
            new BeanBuilder() //
                    .id("budo-permission-aspectj") //
                    .type(AspectJExpressionPointcutAdvisor.class) //
                    .property("expression", this.getAopPatterns()) //
                    .ref("advice", "UserPermissionCheckMethodInterceptor")//
                    .registerTo(applicationContext);
        }
    }

    protected void registryWebInterceptor(BeanDefinitionRegistry registry) {
        if (StringUtil.isNullOrEmpty(this.getMvcPatterns())) {
            log.error("#56 registryWebInterceptor return, this=" + this);
            return;
        }

        // UserPermissionCheckHandlerInterceptor
        new BeanBuilder() //
                .id("UserPermissionCheckHandlerInterceptor") //
                .type(UserPermissionCheckHandlerInterceptor.class) //
                .registerTo(registry);

        String includePatterns = null;
        if ("auto".equalsIgnoreCase(this.getMvcPatterns())) {
            includePatterns = null;
        } else {
            includePatterns = this.getMvcPatterns();
        }

        // Map
        new BeanBuilder() //
                .id("mapped-budo-permission-mvc-interceptor") //
                .type(MappedInterceptor.class) //
                .constructorArg(includePatterns) // includePatterns
                .constructorArgReference("UserPermissionCheckHandlerInterceptor") // interceptor
                .registerTo(registry);
    }

    private void registerPermissionApiFilter(ApplicationContext applicationContext) {
        new BeanBuilder() //
                .id("BudoPermissionSupportFilter") //
                .type(BudoPermissionSupportFilter.class) //
                .registerTo(applicationContext);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        log.info("#20 postProcessBeanFactory, beanFactory=" + ObjectUtils.identityToString(beanFactory));
    }
}